package it.infodea.tapestrydea.services.jcr.nodes.wrappers;

import it.infodea.tapestrydea.services.RepositoryAssetDispatcher;
import it.infodea.tapestrydea.utils.JcrDeaConstants;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import javax.jcr.ItemExistsException;
import javax.jcr.Node;
import javax.jcr.PathNotFoundException;
import javax.jcr.RepositoryException;
import javax.jcr.UnsupportedRepositoryOperationException;
import javax.jcr.ValueFormatException;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.version.Version;
import javax.jcr.version.VersionException;
import javax.jcr.version.VersionHistory;
import javax.jcr.version.VersionIterator;

import org.apache.tapestry5.services.Request;

/**
 * Classe wrapper per i nodi di tipo dea:file. La classe funziona sulla
 * configurazione standard per la gestione dei files in cui il nodo principale è
 * di tipo dea:file e ne contiene uno (jcr:content) di tipo nt:resource. Oltre a
 * consentire l'accesso alle principali proprietà del nodo (o del suo child), la
 * classe mette a disposizione due metodi ( {@link #getVersionHistory} e
 * {@link #restoreVersion(Version)} ) attraverso i quali è possibile accedere
 * alle versioni precedenti del file e ripristanarle.
 * 
 * @author bobpuley
 * 
 */
public class JcrFileNode extends JcrAbstractNode {

	/**
	 * Il costuttore della classe si aspetta di rivere un node jcr del tipo
	 * dea:file, se il parametro è nullo, allora tutti i metodi restituiranno
	 * null (o il default per i tipi primitivi)
	 * 
	 * @param surroundedNode
	 *            il nodo dea:file che rappresenta un file nel repository
	 * @throws RepositoryException
	 *             viene propagata un'eccezione nel caso in cui il repository
	 *             andasse in errore. Questo può succedere perchè, se il nodo
	 *             non è nullo, allora si salva in una field anche la sessione
	 *             ottenuta dal nodo stesso. La richiesta dell'istanza della
	 *             sessione, può propagare una <code>RepositoryException</code>.
	 */
	public JcrFileNode(Node surroundedNode) throws RepositoryException {
		super(surroundedNode);
	}

	/**
	 * Rinomina il nodo e aggiorna la data di modifica.
	 * 
	 * @param newName
	 *            il nuovo nome del file node
	 * @throws ValueFormatException
	 *             questa eccezione viene propagata se il <code>newName</code>
	 *             contiene dei caratteri non validi (es: <b>/</b>).
	 * @throws VersionException
	 *             se si sta cercando di aggiornare un nodo la cui versione nel
	 *             fattempo è cambiata
	 * @throws LockException
	 *             se il nodo è già estratto da qualche utente
	 * @throws ConstraintViolationException
	 * @throws RepositoryException
	 *             se fallisce il save delle modifiche
	 */
	public void rename(String newName) throws ValueFormatException, VersionException, LockException, ConstraintViolationException,
			RepositoryException {

		if (surroundedNode != null) {
			// chiama il rename della superclasse
			super.rename(newName);
			// imposta la data attuale come data di ultima modifica
			setLastModified();
			jcrSession.save();
		}

	}

	/**
	 * Questo metodo consente di aggiornare il contenuto binario di un nodo file
	 * lo stream deve essere non nullo e aperto. Il metodo non chiude lo stream
	 * dopo averlo letto.
	 * 
	 * @param stream
	 *            lo stream da cui leggere i dati binari per aggiornare il nodo
	 *            file
	 * @throws ValueFormatException
	 * @throws VersionException
	 *             se si sta cercando di aggiornare un nodo la cui versione nel
	 *             fattempo è cambiata
	 * @throws LockException
	 *             se il nodo è già estratto da qualche utente
	 * @throws ConstraintViolationException
	 * @throws RepositoryException
	 *             se fallisce il save delle modifiche
	 */
	public void updateDataStream(InputStream stream) throws ValueFormatException, VersionException, LockException,
			ConstraintViolationException, RepositoryException {

		getContent().setProperty(JcrDeaConstants.JCR_DATA, stream);
		setLastModified();
		jcrSession.save();

	}

	/**
	 * Restituisce il mimetype registrato per il nodo. Il mimetype viene
	 * impostato, programmaticamente, quando il nodo viene creato.
	 * 
	 * @see it.infodea.JcrFolderNode.server.dto.jcr.FolderNodeDTO#createFile(String,
	 *      InputStream)
	 * 
	 * @return String il mime-type del nodo. Il default value è
	 *         application/octet-stream
	 * @throws ValueFormatException
	 * @throws PathNotFoundException
	 *             se la property <b>jcr:mimeType</b> non esiste sul nodo
	 * @throws RepositoryException
	 *             se fallisce il save delle modifiche
	 */
	public String getMimeType() throws ValueFormatException, PathNotFoundException, RepositoryException {

		return getContent().getProperty(JcrDeaConstants.JCR_MIME_TYPE).getString();

	}

	public String getOwner() throws ValueFormatException, PathNotFoundException, RepositoryException {
		return surroundedNode.getProperty(JcrDeaConstants.DEA_OWNER).getString();
	}

	/**
	 * Restituisce l'encoding del nodo. L'encoding viene impostato,
	 * programmaticamente, quando il nodo viene creato.
	 * 
	 * @see it.infodea.JcrFolderNode.server.dto.jcr.FolderNodeDTO#createFile(String,
	 *      InputStream)
	 * 
	 * @return String l'encoding del contenuto binario dl nodo
	 * @throws ValueFormatException
	 * @throws PathNotFoundException
	 *             se la property <b>jcr:encoding</b> non esiste sul nodo
	 * @throws RepositoryException
	 */
	public String getEncoding() throws ValueFormatException, PathNotFoundException, RepositoryException {

		return getContent().getProperty(JcrDeaConstants.JCR_ENCODING).getString();

	}

	/**
	 * Restituisce l'ultima data di modifica del nodo
	 * 
	 * @return Calendar la data dell'ultima modifica del nodo
	 * @throws ValueFormatException
	 * @throws PathNotFoundException
	 *             se la property <b>jcr:lastModified</b> non esiste sul nodo
	 * @throws RepositoryException
	 */
	public Calendar getLastModified() throws ValueFormatException, PathNotFoundException, RepositoryException {

		return getContent().getProperty(JcrDeaConstants.JCR_LAST_MODIFIED).getDate();

	}

	/**
	 * Questo metodo reatituisce lo stream del dato binario associato al nodo.
	 * E' responsabilità del chiamante chiudere lo stream dopo averlo usato.
	 * 
	 * @return InputStream lo stream del dato binario
	 * @throws ValueFormatException
	 * @throws PathNotFoundException
	 *             se il child node <b>jcr:content</b> o la sua property
	 *             <b>jcr:data</b> non esistono.
	 * @throws RepositoryException
	 */
	public InputStream getDataStream() throws ValueFormatException, PathNotFoundException, RepositoryException {

		return getContent().getProperty(JcrDeaConstants.JCR_DATA).getStream();

	}

	/**
	 * Restituisce la lista delle versioni ordinate dalla più vecchia alla più
	 * recente.
	 * 
	 * @return List<Version> la lista delle versioni del nodo
	 * @throws RepositoryException
	 */
	public List<Version> getVersionHistory() throws RepositoryException {

		List<Version> results = new ArrayList<Version>();
		VersionHistory history = surroundedNode.getVersionHistory();
		for (VersionIterator it = history.getAllVersions(); it.hasNext();) {
			results.add(it.nextVersion());
		}
		return results;

	}

	/**
	 * Consente di ripristinare una differente versione del nodo. Il metodo
	 * effettua il restore del nodo con la versione fornita solo se la versione
	 * è una versione del nodo stesso
	 * 
	 * @param version
	 *            la versione target, dovrebbe essere ottenuta chiamando
	 *            {@link #getVersionHistory()}
	 * @throws VersionException
	 * @throws ItemExistsException
	 * @throws UnsupportedRepositoryOperationException
	 * @throws LockException
	 * @throws RepositoryException
	 */
	public void restoreVersion(Version version) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException,
			LockException, RepositoryException {

		String originalFileUuid = version.getProperty("jcr:frozenUuid").getString();
		if (getUUID().equals(originalFileUuid)) {
			surroundedNode.restore(version, true);
			jcrSession.save();
		}

	}

	/*
	 * restituisce il nodo content del file corrente
	 */
	private Node getContent() throws PathNotFoundException, RepositoryException {
		return surroundedNode.getNode(JcrDeaConstants.JCR_CONTENT);
	}

	/*
	 * imposta il nuovo valore lastModified
	 */
	private void setLastModified() throws ValueFormatException, VersionException, LockException, ConstraintViolationException,
			RepositoryException, PathNotFoundException {

		getContent().setProperty(JcrDeaConstants.JCR_LAST_MODIFIED, Calendar.getInstance());
	}

	/**
	 * Restituisce una URL costruita per indicare che la risorsa va richiesta al
	 * repository (es, asset/jcr/folder/file.ext). L'apposito dispatcher
	 * {@link RepositoryAssetDispatcher} si occuperà di prelevare la risorsa dal
	 * repository
	 * 
	 * @param request
	 * @return
	 * @throws RepositoryException
	 */
	public String toClientURL(Request request) throws RepositoryException {
		return request.getContextPath() + RepositoryAssetDispatcher.REPOSITORY_PATH_PREFIX + getPath();
	}

}
